#    Copyright 2012 Tri-County Electric Cooperative, St. Matthews, South Carolina
#    This file is part of COOPIP.
#    COOPIP is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#    COOPIP is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#    You should have received a copy of the GNU General Public License
#    along with COOPIP.  If not, see <http://www.gnu.org/licenses/>.

import sys
NeededPaths=['/opt/coopip/djangoproject/include', '/opt/coopip/djangoproject', '/opt/cis', '/opt/oms/', '/opt/amr', '/opt/gis','/opt/gos', '/opt']
for path in NeededPaths:
    if(not path in sys.path):
        sys.path.append(path)
import httplib
import os, csv, shutil, datetime, time, reportlab, djangoproject.settings, datetime
from django.contrib.gis.db import models
from django.contrib.gis.geos import *
from django.http import HttpResponse
from django.contrib.gis import admin
import _mysql as mysql
from django.db import transaction
import django, urllib
import COOPIP_Form
import CIS, AMR, GIS, GOS  #Comment this out before doing a syncdb
from ftplib import FTP
import cPickle
import ipbase



class CommunicationCommandName(models.Model):
    using='default'
    Name=models.TextField(db_column='name', unique=True)
    class Meta:
        db_table=u'communicationcommandname'
        app_label='djangoproject'

    @staticmethod
    def GetOrCreate(Name):
        CommunicationCommandNames=CommunicationCommandName.objects.filter(Name=Name)
        if(CommunicationCommandNames):
            return CommunicationCommandNames[0]
        else:
            New=CommunicationCommandName()
            New.Name=Name
            New.save()
            return New
    @staticmethod
    def CreateInitialCommandNames():
        CommunicationCommandName.GetOrCreate('PingMeter')  #Ping a Meter, String includes the MeterNumber
        CommunicationCommandName.GetOrCreate('AlarmEventAcknowledge')  
        CommunicationCommandName.GetOrCreate('AlarmEventClose') 
        CommunicationCommandName.GetOrCreate('AlarmArm') 
        CommunicationCommandName.GetOrCreate('AlarmDisarm') 



class CommunicationCommandStatus(models.Model):
    using='default'
    Name=models.TextField(db_column='name', unique=True)
    class Meta:
        db_table=u'communicationcommandstatus'
        app_label='djangoproject'

    @staticmethod
    def GetOrCreate(Name):
        CommunicationCommandStatuses=CommunicationCommandStatus.objects.filter(Name=Name)
        if(CommunicationCommandStatuses):
            return CommunicationCommandStatuses[0]
        else:
            New=CommunicationCommandStatus()
            New.Name=Name
            New.save()
            return New
    @staticmethod
    def CreateInitialStatuses():
        CommunicationCommandStatus.GetOrCreate('New')  #Command has not ben verified yet
        CommunicationCommandStatus.GetOrCreate('Verified') #Command has been verified from a security perspective, but processing has not started yet
        CommunicationCommandStatus.GetOrCreate('Rejected') #Command has been rejected due to security issues (too many commands in too short of a time for example)
        CommunicationCommandStatus.GetOrCreate('SentToAMR') #Command is waiting for a response to AMR
        CommunicationCommandStatus.GetOrCreate('Completed') #Command has finished executing, may need to be sent to COOPWeb depending on the Pending Indicator



class CommunicationCommand(models.Model):
    using='default'
    CommunicationCommandName=models.ForeignKey('CommunicationCommandName', db_column='communicationcommandname')
    CommandString=models.TextField(db_column='commandstring', null=True, blank=True)
    CreateDateTime=models.DateTimeField(db_column='createdatetime')
    COOPWebCommunicationCommandIdentifier=models.IntegerField(db_column='coopwebcommunicationcommandidentifier', unique=True)
    COOPWebUsername=models.TextField(db_column='coopwebusername', null=True, blank=True)
    CommunicationCommandStatus=models.ForeignKey('CommunicationCommandStatus', db_column='communicationcommandstatus_id')
    CommunicationCommandStatusLastUpdateDateTime=models.DateTimeField(db_column='communicationcommandstatuslastupdatedatetime', null=True, blank=True)
    ResponsePendingIndicator=models.NullBooleanField(db_column='responsependingindicator', null=True, blank=True)
    ResponseString=models.TextField(db_column='responsestring', null=True, blank=True)
    ResponseDateTime=models.DateTimeField(db_column='responsedatetime', null=True, blank=True)
    class Meta:
        db_table=u'communicationcommand'
        app_label='djangoproject'

    @staticmethod
    def ProcessNew():
        CommunicationCommands=CommunicationCommand.objects.filter(CommunicationCommandStatus__Name='New')
        for i in CommunicationCommands:
            RuleVerificationIndicator = i.VerifyRules()
            if(not RuleVerificationIndicator):  #This means a command failed the rules validation check
                communicationCommandStatus = CommunicationCommandStatus.objects.filter(Name='Rejected')[0]
                i.ResponseString='Rejected'
                i.ResponsePendingIndicator=True
            else:
                communicationCommandStatus = CommunicationCommandStatus.objects.filter(Name='Verified')[0]
            i.CommunicationCommandStatus = communicationCommandStatus
            i.CommunicationComandStatusLastUpdateDateTime=datetime.datetime.now()
            i.save()
    def ExtractAlarmAndDescriptionOrReject(self):
        Alarmid = self.CommandString.split('|')[0]
        Description = self.CommandString.split('|')[1]
        print "The Alarm ID IS:  " + str(int(Alarmid))
        alarm=djangoproject.models.networkdevice.NetworkDevice.objects.filter(id=int(Alarmid))[0]
        try:
            Alarmid = self.CommandString.split('|')[0]
            Description = self.CommandString.split('|')[1]
            alarm=djangoproject.models.networkdevice.NetworkDevice.objects.filter(id=int(Alarmid))[0]
        except:
            self.CommunicationCommandStatus = CommunicationCommandStatus.objects.filter(Name='Rejected')[0]
            self.ResponseString='Rejected'
            self.ResponsePendingIndicator=True
            self.CommunicationComandStatusLastUpdateDateTime=datetime.datetime.now()
            self.save()
            alarm=None
            Description = None
        return alarm, Description
    @staticmethod
    def ProcessVerified():
        CommunicationCommands=CommunicationCommand.objects.filter(CommunicationCommandStatus__Name='Verified')
        PingWaitingMetersIndicator=False
        for i in CommunicationCommands:
            if(i.CommunicationCommandName.Name=='PingMeter'):
                meters=ipbase.Meter.objects.filter(SerialNumber=i.CommandString)
                if(not meters):
                    i.ResponseString='Meter Not Found:  ' + str(i.CommandString)
                    i.CommunicationCommandStatus=CommunicationCommandStatus.objects.filter(Name='Completed')[0]
                    i.ResponsePendingIndicator=True
                    i.CommunicationCommandStatusLastUpdateDateTime=datetime.datetime.now()
                    i.save()
                    continue
                meters[0].Ping()
                i.CommunicationCommandStatus=CommunicationCommandStatus.objects.filter(Name='SentToAMR')[0]
                i.CommunicationCommandStatusLastUpdateDateTime=datetime.datetime.now()
                i.save()
                PingWaitingMetersIndicator=True
            elif(i.CommunicationCommandName.Name == 'AlarmEventAcknowledge'): 
                alarm, Description = i.ExtractAlarmAndDescriptionOrReject()
                if(not alarm):
                    continue
                Recipients=djangoproject.models.networkdevice.AlarmContactGroup.objects.order_by('PriorityLevel')[0].GetRecipients()
                djangoproject.models.networkdevice.AlarmEventNotification.SendNotification(alarmEventHistory=None, Recipients=Recipients, NotificationLevel = 1, Subject=str(alarm) + ' disarm request by:  ' + str(i.COOPWebUsername),
                                                        Body=str(alarm) + ' Event Acknowledge request by:  ' + str(i.COOPWebUsername) + '\nDateTime:  ' + str(i.CreateDateTime) + '\nNotes:  ' + Description, AlarmEventDescription=Description)
                alarm.PendingDisarmUsername = str(i.COOPWebUsername)
                alarm.save()
                OpenEvent = alarm.GetOpenAlarmEventHistory()
                if(OpenEvent and not OpenEvent.AcknowledgeDateTime):
                    OpenEvent.AcknowledgeDateTime = datetime.datetime.now()
                    OpenEvent.AcknowledgeUsername = str(i.COOPWebUsername)
                    OpenEvent.PendingAcknowledgeNotification = True
                    OpenEvent.Description = str(OpenEvent.Description + Description) 
                    OpenEvent.save()
                i.ResponseString='Alarm Acknowledge Event Request Processed.'
                i.CommunicationCommandStatus=CommunicationCommandStatus.objects.filter(Name='Completed')[0]
                i.ResponsePendingIndicator=True
                i.CommunicationCommandStatusLastUpdateDateTime=datetime.datetime.now()
                i.save()
            elif(i.CommunicationCommandName.Name == 'AlarmEventClose'):
                alarm, Description = i.ExtractAlarmAndDescriptionOrReject()
                if(not alarm):
                    continue
                Recipients=djangoproject.models.networkdevice.AlarmContactGroup.objects.order_by('PriorityLevel')[0].GetRecipients()
                djangoproject.models.networkdevice.AlarmEventNotification.SendNotification(alarmEventHistory=None, Recipients=Recipients, NotificationLevel = 1, Subject=str(alarm) + ' disarm request by:  ' + str(i.COOPWebUsername),
                                                        Body=str(alarm) + ' Event Closed request by:  ' + str(i.COOPWebUsername) + '\nDateTime:  ' + str(i.CreateDateTime) + '\nNotes:  ' + Description, AlarmEventDescription=Description)
                alarm.PendingArmUsername = str(i.COOPWebUsername)
                alarm.save()
                OpenEvent = alarm.GetOpenAlarmEventHistory()

                if(OpenEvent and not OpenEvent.CloseDateTime):
                    OpenEvent.CloseDateTime = datetime.datetime.now()
                    OpenEvent.CloseUsername = str(i.COOPWebUsername)
                    OpenEvent.Description = str(OpenEvent.Description + Description) 
                    OpenEvent.save()


                i.ResponseString='Alarm Close Event Request Processed.'
                i.CommunicationCommandStatus=CommunicationCommandStatus.objects.filter(Name='Completed')[0]
                i.ResponsePendingIndicator=True
                i.CommunicationCommandStatusLastUpdateDateTime=datetime.datetime.now()
                i.save()
            elif(i.CommunicationCommandName.Name == 'AlarmArm'):
                alarm, Description = i.ExtractAlarmAndDescriptionOrReject()
                if(not alarm):
                    continue
                Recipients=djangoproject.models.networkdevice.AlarmContactGroup.objects.order_by('PriorityLevel')[0].GetRecipients()
                djangoproject.models.networkdevice.AlarmEventNotification.SendNotification(alarmEventHistory=None, Recipients=Recipients, NotificationLevel = 1, Subject=str(alarm) + ' arm request by:  ' + str(i.COOPWebUsername),
                                                        Body=str(alarm) + ' arm request by:  ' + str(i.COOPWebUsername) + '\nDateTime:  ' + str(i.CreateDateTime) + '\nNotes:  ' + Description, AlarmEventDescription=Description)
                alarm.PendingArmUsername = str(i.COOPWebUsername)
                alarm.save()
                i.ResponseString='Alarm Arm Request Processed.'
                i.CommunicationCommandStatus=CommunicationCommandStatus.objects.filter(Name='Completed')[0]
                i.ResponsePendingIndicator=True
                i.CommunicationCommandStatusLastUpdateDateTime=datetime.datetime.now()
                i.save()
            elif(i.CommunicationCommandName.Name == 'AlarmDisarm'):
                alarm, Description = i.ExtractAlarmAndDescriptionOrReject()
                if(not alarm):
                    continue
                Recipients=djangoproject.models.networkdevice.AlarmContactGroup.objects.order_by('PriorityLevel')[0].GetRecipients()
                djangoproject.models.networkdevice.AlarmEventNotification.SendNotification(alarmEventHistory=None, Recipients=Recipients, NotificationLevel = 1, Subject=str(alarm) + ' disarm request by:  ' + str(i.COOPWebUsername),
                                                        Body=str(alarm) + ' disarm request by:  ' + str(i.COOPWebUsername) + '\nDateTime:  ' + str(i.CreateDateTime) + '\nNotes:  ' + Description, AlarmEventDescription=Description)
                alarm.PendingDisarmUsername = str(i.COOPWebUsername)
                alarm.save()
                i.ResponseString='Alarm Disarm Request Processed.'
                i.CommunicationCommandStatus=CommunicationCommandStatus.objects.filter(Name='Completed')[0]
                i.ResponsePendingIndicator=True
                i.CommunicationCommandStatusLastUpdateDateTime=datetime.datetime.now()
                i.save()





        if(PingWaitingMetersIndicator):
            ipbase.MeterPing().PingWaitingMeters()
    @staticmethod
    def ProcessOutToAMR():
        CommunicationCommands=CommunicationCommand.objects.filter(CommunicationCommandStatus__Name='SentToAMR')
        for i in CommunicationCommands:
            Now=datetime.datetime.now()
            NowString=Now.strftime('%I:%M%p %b %d, %Y').ljust(25)
            
            if(i.CommunicationCommandName.Name=='PingMeter'):
                meters=ipbase.Meter.objects.filter(SerialNumber=i.CommandString)
                if(not meters):
                    i.ResponseString=NowString + 'Meter Not Found:  '.ljust(20) + str(i.CommandString)
                    i.CommunicationCommandStatus=CommunicationCommandStatus.objects.filter(Name='Completed')[0]
                    i.ResponsePendingIndicator=True
                    i.CommunicationCommandStatusLastUpdateDateTime=Now
                    i.save()
                    continue
                if(meters[0].LastPingResponseDateTime) and (meters[0].LastPingResponseDateTime > i.CommunicationCommandStatusLastUpdateDateTime - datetime.timedelta(minutes=1)):
                    if(meters[0].LastPingSuccessIndicator):
                        i.ResponseString=NowString + 'Meter ping succeeded:  '.ljust(22) + str(i.CommandString)
                        i.CommunicationCommandStatus=CommunicationCommandStatus.objects.filter(Name='Completed')[0]
                        i.ResponsePendingIndicator=True
                        i.CommunicationCommandStatusLastUpdateDateTime=Now
                        i.save()
                    else:
                        i.ResponseString=NowString + 'Meter ping failed:  '.ljust(22) + str(i.CommandString)
                        i.CommunicationCommandStatus=CommunicationCommandStatus.objects.filter(Name='Completed')[0]
                        i.ResponsePendingIndicator=True
                        i.CommunicationCommandStatusLastUpdateDateTime=Now
                        i.save()
                else: #Timeout if meter has not pinged in a long time
                    if(datetime.datetime.now() - datetime.timedelta(hours=24) > i.CommunicationCommandStatusLastUpdateDateTime):
                        i.ResponseString=NowString + 'Meter ping timed-out:  '.ljust(20) + str(i.CommandString)
                        i.CommunicationCommandStatus=CommunicationCommandStatus.objects.filter(Name='Completed')[0]
                        i.ResponsePendingIndicator=True
                        i.CommunicationCommandStatusLastUpdateDateTime=Now
                        i.save()

    def VerifyRules(self):
        Rejected=CommunicationCommandStatus.objects.filter(Name='Rejected')[0]
        #Rule 1:  No More than 1000 commands of any type within an hour, excluding rejected commands
        StartDateTime=self.CreateDateTime-datetime.timedelta(hours=1)
        EndDateTime=self.CreateDateTime+datetime.timedelta(hours=1)
        NumberOfCommands=CommunicationCommand.objects.filter(CreateDateTime__gte=StartDateTime).filter(CreateDateTime__lte=EndDateTime).exclude(CommunicationCommandStatus=Rejected).count()
        if(NumberOfCommands > 1000):  #max of 1000 meter pings/attempts per hour for now
            return False

        #Rule 2:  No More than 50 identical commands in the past 15 minutes [or queued up in the future]
        StartDateTime=self.CreateDateTime-datetime.timedelta(minutes=15)
        EndDateTime=self.CreateDateTime+datetime.timedelta(minutes=5)
        NumberOfCommands=CommunicationCommand.objects.filter(CreateDateTime__gte=StartDateTime).filter(CreateDateTime__lte=EndDateTime).exclude(CommunicationCommandStatus=Rejected).filter(CommandString=self.CommandString).count()
        if(NumberOfCommands > 50):
            return False

        #Rule 3:  maximum length of the command string
        #raise(Exception(str(self.CommandString)))
        if(len(self.CommandString) > 25000):
            return False
        return True















